import G from "../Utilities/Global";
import SpriteItem from "../UI/SpriteItem";
import EntityStruct from "../Entity/EntityStruct";
import EntityTypes from "../Constants/EntityTypes";
import C from "../Configs/Config";
import MapAssetStruct from "../Structs/MapAssetStruct";
import Constants from "../Constants/Constants";
import MapData from "./MapData";
import IAsset from "../Interfaces/IAsset";
import MonsterAssetStruct from "../Structs/MonsterAssetStruct";
import Oriented from "../Constants/Oriented";
import CastType from "../Constants/CastType";

/**
 * 地图渲染器 只负责画地图
 * created by Five on 2018-11-8 15:09:22
 */

export default class MapRender extends cc.Component {
    private _runningData: MapData;
    private _isEditMode: boolean;
    private _secondLayer: Array<cc.Node>;

    // 一些特殊物件,单独保存一下
    private _door: any = null;

    constructor() {
        super();
    }

    public SetData(d: MapData, isEdit: boolean) {
        this._runningData = d;
        this._isEditMode = isEdit;
        this._secondLayer = new Array<cc.Node>();
        this._runningData.Entities.forEach(e => {
            this.addToEmpty(e.Location, e.Asset);
        });
        if (this._isEditMode) {
            this._runningData.InvisEnt.forEach(e => {
                this.addToEmpty(e.Location, e.Asset);
            });
            this._runningData.Monsters.forEach(e => {
                this.addToEmpty(e.Location, e.Asset);
            });
        }
    }

    /**
     * 添加一块
     * @param loc 位置
     * @param asset 资源
     */
    public AddTile(loc: number, asset: IAsset): boolean {
        // 判断原来的位置有没有东西
        // 还要考虑到初次创建时,有一个顺序问题,这个想办法交给数据初始化时来处理
        let entity = this._runningData.GetWallByLocation(loc, true);
        if (entity) {
            return this.changeOrignal(loc, asset, entity);
        } else {
            return this.addToEmpty(loc, asset);
        }
    }

    // 在原本是空白的地方加东西
    private addToEmpty(loc: number, asset: IAsset): boolean {
        this.addToCanvas(loc, asset);
        return true;
    }

    // 在原本有东西的地方加东西
    private changeOrignal(loc: number, asset: IAsset, entity: EntityStruct): boolean {
        // 如果是一个可以破坏的部件
        if (C().IsDestoryable(entity.Asset)) {
            if (!C().IsMonster(asset) && (asset as MapAssetStruct).effect > 0) {
                // 下面就是见证奇迹的时刻,插入!
                entity.Asset.enumber = asset.id;
                // 运行模式下才需要展示这个东西
                if (this._isEditMode)
                    this.add2Child(loc, asset);
                return true;
            }
        }

        // 如果是同样的,并且是个怪物,转个向就可以了
        if (entity && entity.Asset.id == asset.id) {
            if (C().IsMonster(entity.Asset) || C().IsMonsterBorn(entity.Asset))
                this.changeOriented(entity);
            return false;
        }
        // 把原来的删了,加上新的
        this.RemoveTile(loc);
        this.addToCanvas(loc, asset);
        return true;
    }

    // 旋转
    private changeOriented(entity: EntityStruct) {
        let asset: MonsterAssetStruct = entity.Asset;
        let loc: number = entity.Location;
        if (asset.oriented == Oriented.LEFT)
            asset.oriented = Oriented.RIGHT;
        else
            asset.oriented = Oriented.LEFT;
        let mo = this.GetTile(loc).getChildByName(loc + "sprite");
        this.renderOriented(mo, asset.oriented);
    }

    // 旋转图片
    private renderOriented(node: cc.Node, ori: Oriented) {
        cc.log('rederdoriented');            
        node.is3DNode = true;
        node.eulerAngles = ori == Oriented.RIGHT ? Constants.OppsiteRotate : cc.Vec3.ZERO;
    }

    /**
     * return 能不能炸
     * @param position 位置
     * @param destory 造还是毁
     */
    public BoomTile(position: cc.Vec2, destory?: boolean): any {
        // 先计算出在哪
        let loc = G().Calc.PositionToLocation(position, this._runningData.Size);
        // 判断有没有东西
        let entity = this._runningData.GetEntityByPosition(position, true);
        if (destory) {
            if (entity && C().IsDestoryable(entity.Asset))
                return [this.RemoveTile(loc), loc, CastType.DESTORY];
            return [-1, loc, CastType.FAILED];
        }
        // 造/毁
        if (entity) {
            if (C().IsDestoryable(entity.Asset)) {
                return [this.RemoveTile(loc), loc, CastType.DESTORY];
            }
            if (C().IsTypeOf(entity.Asset, EntityTypes.HIDE)) {
                this.RemoveTile(loc);
                this.AddTile(loc, C().MapAsset.GetAsset(Constants.WALL_ASSETID));
                return [-1, loc, CastType.MAKE];
            }
            return [-1, loc, CastType.FAILED];
        } else {
            this.AddTile(loc, C().MapAsset.GetAsset(Constants.WALL_ASSETID));
        }
        return [-1, loc, CastType.MAKE];
    }

    /**
     * 删除某些块
     * 有多种情况, 删除自己,删除编辑模式下所包含的子渲染对象
     * @param arr Array<number>
     */
    public RemoveTile(loc: number, onlyChild: boolean = false): number {
        let child = this.GetTile(loc);
        let result = 0;
        if (child) {
            let grandChild = child.getChildByName(loc + "");
            if (grandChild) {
                child.removeChild(grandChild);
                let e = this._runningData.GetWallByLocation(loc);
                e.Asset.enumber = 0;
            }
            if (!onlyChild) {
                this.node.removeChild(child);
                result = this._runningData.RemoveEntity(loc);
                G().AStar.changeOne(loc);
            }
        }
        return result;
    }

    /**
     * 删除某些块
     * @param arr Array<number>
     */
    public RemoveTiles(arr: any) {
        arr.forEach(element => {
            this.RemoveTile(element);
        });
    }


    // 在可破坏对象上挂个东西
    private async add2Child(loc: number, asset: IAsset) {
        // 先把原来的东西删了
        this.RemoveTile(loc, true);
        // 找出原始位置的东西
        let parent = this.GetTile(loc);
        // 子节点不需要带碰撞了
        let node: cc.Node = new cc.Node();
        node.name = loc + "";
        let sprite = node.addComponent(SpriteItem);
        sprite.SetSpriteName(asset.asset);
        parent.addChild(node);
    }

    // 在画布上挂个东西
    private async addToCanvas(loc: number, asset: IAsset) {
        // 计算坐标
        let position = G().Calc.LocationToPostion(loc, this._runningData.Size)
        // 带碰撞的节点
        let node: cc.Node;
        if (C().IsTypeOf(asset, EntityTypes.COLLECT))
            node = await G().Res.GetRes(Constants.PRE_COLLECT) as cc.Node;
        else
            node = await G().Res.GetRes(Constants.PRE_OBJECT) as cc.Node;
        // 渲染节点
        let sprite = new cc.Node();
        sprite.addComponent(SpriteItem).SetSpriteName(asset.asset);
        node.addChild(sprite, -1, loc + "sprite");
        node.name = loc + "";
        if (this._isEditMode)
            node.getChildByName("loc").getComponent(cc.Label).string = loc + "";
        else
            node.getChildByName("loc").active = false;
        node.position = position;
        this.node.addChild(node);
        // 如果是门,记录一下门的位置
        if (asset.id == Constants.ASSET_DOOR)
            this._door = [loc, node];
        this.addToData(loc, asset)
        G().AStar.changeOne(loc);

        if (!C().IsTypeOf(asset, EntityTypes.COLLISION)) {
            node.removeComponent(cc.PhysicsBoxCollider);
            node.removeComponent(cc.RigidBody);
        }
        if (C().IsMonster(asset)) {
            // 如果是怪物,把怪物的朝向改一下
            this.renderOriented(sprite, (asset as MonsterAssetStruct).oriented);
        }
        else if (this._isEditMode) {
            // 如果有子节点。挂载上去
            let mapAsset = asset as MapAssetStruct
            if (mapAsset.enumber > 0 && mapAsset.id == Constants.WALL_ASSETID) {
                let add = C().MapAsset.GetAsset(mapAsset.enumber);
                this.add2Child(loc, add);
            }
        }
    }

    /**
     * 添加到记录缓存中
     * @param loc 位置索引
     */
    private addToData(loc: number, asset: IAsset) {
        this._runningData.PushEntity(new EntityStruct(asset, loc));
    }

    /**
     * 拿个块给外面用
     * @param loc 
     */
    public GetTile(loc: number): cc.Node {
        return this.node.getChildByName(loc + "");
    }

    /**
     * 把门把手给我
     */
    public GetDoor(): any {
        return this._door;
    }
}